---
title: 'From Firebase'
description: 'Learn how to migrate from Firebase Authentication to Nile Auth'
---

import AuthMigrationTesting from '/snippets/auth_migration_testing.mdx';
import AuthMigrationBestPractices from '/snippets/auth_migration_best_practices.mdx';
import AuthMigrationSocial from '/snippets/auth_migration_social.mdx';

If you are currently using Firebase Authentication, this guide will help you migrate to Nile Auth.
Migrating production applications from one auth provider to another is a non-trivial task,
and we recommend you do thorough testing.

Before you begin, you'll need a Nile account. If you don't have one, you can sign up [here](https://console.thenile.dev).

## Migration Steps

<Steps>

<Step title="Export User Data from Auth0">
This step is necessary if you use Firebase's email/password or passwordless login methods. 
In this case, your users' data is stored in Firebase, and you will need to export it.

Firebase has a [friendly CLI tool for exporting user data](https://firebase.google.com/docs/cli/auth#auth-export), but unfortunately, it does not contain the organizations that users belong to.

Therefore, we need to use the Firebase Admin SDK to first list all the organizations, and then for each organization, list all the users.

This requires installing and configuring the Firebase Admin SDK. Follow the [Firebase Admin SDK setup guide](https://cloud.google.com/identity-platform/docs/install-admin-sdk) for more details.

Here is an example script that exports the tenants and users:

```typescript [expandable]
import * as admin from 'firebase-admin';
import * as fs from 'fs';

async function listAllTenants(nextPageToken, allTenants = []) {
  const result = await admin
    .auth()
    .tenantManager()
    .listTenants(100, nextPageToken);

  allTenants.push(...result.tenants.map((tenant) => tenant.toJSON()));

  if (result.pageToken) {
    return await listAllTenants(result.pageToken, allTenants);
  }

  return allTenants;
}

async function listAllUsers(tenantId, nextPageToken, allUsers = []) {
  const tenantAuth = admin.auth().tenantManager().authForTenant(tenantId);

  try {
    const listUsersResult = await tenantAuth.listUsers(1000, nextPageToken);

    // Add users with their tenant ID
    allUsers.push(
      ...listUsersResult.users.map((user) => ({
        ...user.toJSON(),
        tenantId: tenantId,
      })),
    );

    if (listUsersResult.pageToken) {
      return await listAllUsers(tenantId, listUsersResult.pageToken, allUsers);
    }

    return allUsers;
  } catch (error) {
    console.error(`Error listing users for tenant ${tenantId}:`, error);
    return allUsers;
  }
}

async function exportAllTenantsAndUsers() {
  try {
    // Get all tenants first
    const tenants = await listAllTenants();
    const allUsers = [];

    // For each tenant, get all users
    for (const tenant of tenants) {
      console.log(`Fetching users for tenant: ${tenant.tenantId}`);
      const tenantUsers = await listAllUsers(tenant.tenantId, null);
      allUsers.push(...tenantUsers);
    }

    // Write tenants and users to separate files
    fs.writeFileSync('firebase-tenants.json', JSON.stringify(tenants, null, 2));
    fs.writeFileSync('firebase-users.json', JSON.stringify(allUsers, null, 2));

    console.log(
      'Export completed! Data written to firebase-tenants.json and firebase-users.json',
    );
  } catch (error) {
    console.error('Export failed:', error);
  }
}

// Start the export
exportAllTenantsAndUsers();
```

Firebase supports exporting password hashes, but note that you can only load them into Nile Auth if you are using **bcrypt**.
To determine the password hash parameters used for your project navigate to the `Authentication > Users` section of the Firebase console and click the three dots icon above the list of users.

</Step>

<Step title="Import User Data into Nile Auth">

You'll need a small script to import the user data into Nile Auth.
The script will iterate over the exported user data and create users in Nile Auth.

The following example script shows how to import user data from a JSON file:

```typescript [expandable]
import { Nile } from '@niledatabase/server';
import * as fs from 'fs';

interface FirebaseUser {
  tenantId: string;
  uid: string;
  email: string;
  emailVerified: boolean;
  disabled: boolean;
  metadata: {
    lastSignInTime: string;
    lastSignInIp: string;
    lastLoginAt: string;
    lastLoginIp: string;
  };
  password: string;
  'Updated At': string;
}

interface FirebaseOrganization {
  id: string;
  name: string;
  display_name: string;
  metadata?: Record<string, any>;
}

async function migrateUsers(nile: any) {
  try {
    // Read and parse the JSON file
    const rawData = fs.readFileSync('firebase-users.json', 'utf8');
    const users: FirebaseUser[] = JSON.parse(rawData);
    let newUser;

    // Process each user
    for (const user of users) {
      try {
        // Generate a temporary password (you might want to adjust this strategy)
        const tempPassword = `temp-${Math.random().toString(36).slice(-8)}`;

        // Get the tenant id from the organization name
        const tenant = await nile.db.query(
          `SELECT id FROM tenants WHERE name = $1`,
          [user.tenantId],
        );

        if (!tenant.rows[0]) {
          console.error(`Tenant ${user.tenantId} not found`);
          continue;
        }
        nile.tenant_id = tenant.rows[0].id; // Set the tenant id so the user is created in the correct tenant
        const preferredName = user.Nickname || user.Name;
        const {
          rows: [newUser],
        } = await nile.db.query(
          'insert into users.users (email, password, preferredName) values ($1, $2, $3) returning *',
          [user.email, tempPassword, preferredName],
        );

        // uncomment this to update the password hash in the credentials table
        // only do this if you exported the password hashes in bcrypt format
        // await nile.db.query(
        //   `UPDATE auth.credentials SET payload = jsonb_build_object('crypt', 'crypt-bf/8', 'hash', $1)
        //    WHERE user_id = $2 AND method = 'PASSWORD';`,
        //    [user.Password, newUser.Id]
        // )

        console.log(`Successfully migrated user: ${user.email}`);
        // You might want to store the temporary passwords to communicate them to users
        console.log(`Temporary password for ${user.email}: ${tempPassword}`);
      } catch (error) {
        console.error(`Failed to migrate user ${user.email}:`, error);
        // Continue with next user even if one fails
        continue;
      }
    }
    console.log('Migration completed');
  } catch (error) {
    console.error('Migration failed:', error);
  }
}

// This is necessary only if the organizations don't already exist in Nile
async function migrateTenants(nile: any) {
  try {
    // Read and parse the organizations JSON file
    const rawData = fs.readFileSync('firebase-tenants.json', 'utf8');
    const organizations: FirebaseOrganization[] = JSON.parse(rawData);

    // Process each organization
    for (const org of organizations) {
      try {
        const tenant = await nile.tenants.create({
          name: org.name,
        });

        console.log(`Successfully created tenant: ${org.name}`);
      } catch (error) {
        console.error(`Failed to create tenant ${org.name}:`, error);
        // Continue with next organization even if one fails
        continue;
      }
    }

    console.log('Tenant migration completed');
  } catch (error) {
    console.error('Tenant migration failed:', error);
  }
}

// Single initialization of Nile
async function migrateAll() {
  const nile = Nile({});
  await migrateTenants(nile);
  await migrateUsers(nile);
}

migrateAll();
```

If you did not have the password hashes imported to Nile, your users will need to reset their passwords after the migration (unless you used passwordless authentication) - you can read how to do this in our [password reset docs](/auth/email/password).

<Tip>
For staged migration, or for edge cases, you can implement the fallback strategy in your application. Attempt authentication with 
Nile Auth, and in the failure handler, attempt the Auth0 authentication. If Auth0 authentication succeeds, 
you can insert into `users.users` to create the user.
</Tip>
</Step>
<Step title="Migrate Social Providers">
<AuthMigrationSocial identity_provider="Firebase" />
</Step>
</Steps>

## Testing

<AuthMigrationTesting />

## Best Practices

<AuthMigrationBestPractices />

## Related Topics

- [Auth0 Migration](/auth/migration/fromauth0)
- [User Management](/auth/concepts/users)
- [Security Concepts](/auth/concepts/jwt)
